package com.squareup.picasso;

import static android.os.Process.THREAD_PRIORITY_BACKGROUND;
import static com.squareup.picasso.Stats.CACHE_MISS;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Handler;
import android.os.Looper;
import android.os.Process;
import android.view.ViewTreeObserver;
import android.widget.ImageView;

import com.squareup.picasso.Action.RequestWeakReference;

import java.io.File;
import java.lang.ref.ReferenceQueue;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.WeakHashMap;

public class Picasso {
    public enum LoadedFrom {
        MEMORY(-16711936),
        DISK(-16776961),
        NETWORK(-65536);

        final int debugColor;

        private LoadedFrom(int i) {
            this.debugColor = i;
        }
    }


    public enum Priority {
        LOW,
        NORMAL,
        HIGH
    }


    public static final class Builder {
        public BitmapDiskLruCache bitmapDiskLruCache;
        private final Context context;
        private Downloader downloader;
        private Runnable2AbsRequestTransformer runnable2AbsRequestTransformer;
        private Cache cache;
        private RequestTransformer transformer;

        public Builder(Context context) {
            if (context == null) {
                throw new IllegalArgumentException("Context must not be null.");
            }
            this.context = context.getApplicationContext();
        }

        public final Picasso build() {
            if (this.downloader == null) {
                this.downloader = Utils.createDefaultDownloader();
            }

            if (this.cache == null) {
                this.cache = new LruCache(context);
            }

            if (this.bitmapDiskLruCache == null) {
                File file = Utils.createDefaultCacheDir(context);
                this.bitmapDiskLruCache = new BitmapDiskLruCache(file, Utils.calculateDiskCacheSize(file));
            }

            if (this.runnable2AbsRequestTransformer == null) {
                this.runnable2AbsRequestTransformer = new Runnable2AbsRequestTransformer();
            }

            if (this.transformer == null) {
                this.transformer = RequestTransformer.IDENTITY;
            }

            Stats stats = new Stats(this.cache);
            return new Picasso(context,
                    new Dispatcher(
                            context,
                            Picasso.dispatcherHandler,
                            this.cache,
                            this.bitmapDiskLruCache,
                            stats),
                    this.cache,
                    this.bitmapDiskLruCache,
                    this.transformer,
                    stats);
        }
    }

    final class CleanupThread extends Thread {
        private final ReferenceQueue<?> referenceQueue;
        private final Handler handler;

        CleanupThread(ReferenceQueue referenceQueue, Handler handler) {
            this.referenceQueue = referenceQueue;
            this.handler = handler;
            this.setDaemon(true);
            this.setName("Picasso-refQueue");
        }

        public final void run() {
            Process.setThreadPriority(THREAD_PRIORITY_BACKGROUND);
            try {
                while (true) {
                    RequestWeakReference remove = (RequestWeakReference) this.referenceQueue.remove(1000);
                    if (remove != null) {
                        this.handler.sendMessage(this.handler.obtainMessage(Dispatcher.REQUEST_GCED, remove.action));
                        continue;
                    }
                }
            } catch (InterruptedException e) {
                return;
            } catch (final Exception e) {
                this.handler.post(new Runnable() {
                    @Override
                    public void run() {
                        throw new RuntimeException(e);
                    }
                });
            }
        }
    }

    public interface RequestTransformer {
        RequestTransformer IDENTITY = new RequestTransformer() {
            @Override
            public Request transformRequest(Request request) {
                return request;
            }
        };

        Request transformRequest(Request request);
    }

    static final Handler dispatcherHandler = new DispatcherHandler(Looper.getMainLooper());
    final RequestTransformer requestTransformer;
    final List<RequestHandler> requestHandlerList;
    final Context context;
    public final Dispatcher dispatcher;
    public final Cache cache;
    public final BitmapDiskLruCache bitmapDiskLruCache;
    final ReferenceQueue<Object> referenceQueue;
    final Bitmap.Config resourceId;
    boolean debugging;
    volatile boolean shutdown;
    boolean l;
    private static volatile Picasso singleton = null;
    private final CleanupThread cleanupThread;
    private Stats stats;
    private Map<Object, Action> targetToAction;
    private Map<ImageView, DeferredRequestCreator> targetToDeferredRequestCreator;

    Picasso(Context context, Dispatcher dispatcher, Cache cache, BitmapDiskLruCache bitmapDiskLruCache, RequestTransformer requestTransformer, Stats stats) {
        this.context = context;
        this.dispatcher = dispatcher;
        this.cache = cache;
        this.bitmapDiskLruCache = bitmapDiskLruCache;
        this.requestTransformer = requestTransformer;
        this.resourceId = null;
        ArrayList requestHandlerList = new ArrayList(7);
        requestHandlerList.add(new ResourceRequestHandler(context));
        requestHandlerList.add(new ContactsPhotoRequestHandler(context));
        requestHandlerList.add(new MediaStoreRequestHandler(context));
        requestHandlerList.add(new ContentStreamRequestHandler(context));
        requestHandlerList.add(new AssetRequestHandler(context));
        requestHandlerList.add(new FileRequestHandler(context));
        requestHandlerList.add(new NetworkRequestHandler(stats));
        this.requestHandlerList = Collections.unmodifiableList(requestHandlerList);
        this.stats = stats;
        this.targetToAction = new WeakHashMap();
        this.targetToDeferredRequestCreator = new WeakHashMap();
        this.debugging = false;
        this.shutdown = false;
        this.referenceQueue = new ReferenceQueue<Object>();
        this.cleanupThread = new CleanupThread(this.referenceQueue, Picasso.dispatcherHandler);
        this.cleanupThread.start();
    }

    public static Picasso with(Context context) {
        if (Picasso.singleton == null) {
            synchronized (Picasso.class) {
                if (Picasso.singleton == null) {
                    Picasso.singleton = new Builder(context).build();
                }
            }
        }
        return Picasso.singleton;
    }

    public synchronized static void setSingleton(Picasso picasso) {
        if (Picasso.singleton != null) {
            throw new IllegalStateException("Singleton instance already exists.");
        }
        Picasso.singleton = picasso;
    }

    final Bitmap quickMemoryCacheCheck(String key) {
        Bitmap bitmap = this.cache.get(key);
        if (bitmap != null) {
            this.stats.dispatchCacheHit();
        } else {
            this.stats.handler.sendEmptyMessage(CACHE_MISS);
        }
        return bitmap;
    }

    public final RequestCreator load(Uri uri) {
        return new RequestCreator(this, uri);
    }

    public final void trimGentle(int trimRatio) {
        if (trimRatio > 1 && this.cache instanceof LruCache) {
            if (trimRatio >= 100) {
                ((LruCache) this.cache).trimToSize(-1);
            } else {
                ((LruCache) this.cache).trimToSize(this.cache.maxSize() / trimRatio);
            }
        }
    }

    final void complete(Drawable drawable, LoadedFrom loadedFrom, Action action, Exception ex) {
        if (!action.canceled) {
            if (!action.willReplay) {
                this.targetToAction.remove(action.getTarget());
            }

            if (drawable != null) {
                if (loadedFrom == null) {
                    throw new AssertionError("LoadedFrom cannot be null.");
                }
                action.complete(drawable, loadedFrom);
                return;
            }
            action.error(ex);
        }
    }

    final void submit(Action action) {
        Object target = action.getTarget();
        if (target != null && this.targetToAction.get(target) != action) {
            this.cancelRequest(target);
            this.targetToAction.put(target, action);
        }
        this.enqueueAndSubmit(action);
    }

    final void cancelRequest(Object target) {
        Utils.checkMain();
        Action action = this.targetToAction.remove(target);
        if (action != null) {
            action.cancel();
            this.dispatcher.cancel(action);
        }

        if ((target instanceof ImageView)) {
            DeferredRequestCreator creator = this.targetToDeferredRequestCreator.remove(target);
            if (creator != null) {
                ImageView targetImageView = creator.target.get();
                if (targetImageView != null) {
                    ViewTreeObserver treeObserver = targetImageView.getViewTreeObserver();
                    if (treeObserver.isAlive()) {
                        treeObserver.removeOnPreDrawListener(creator);
                    }
                }
            }
        }
    }

    public final boolean cached(Request request) {
        if (request != null) {
            String key = Utils.createKey(request);
            boolean cached = this.cache != null && this.cache.contains(key);
            if (!cached && this.bitmapDiskLruCache != null) {
                key = com.bytedance.common.utility.aa.md5(key);
                if (bitmapDiskLruCache.diskLruCache != null) {
                    return bitmapDiskLruCache.diskLruCache.contains(key);
                }
                return false;
            } else {
                return cached;
            }
        }
        return false;
    }

    final void enqueueAndSubmit(Action arg4) {
        this.dispatcher.handler.sendMessage(this.dispatcher.handler.obtainMessage(1, arg4));
    }

    public final void trimCritical(int trimRatio) {
        if (trimRatio > 1 && ((this.cache instanceof LruCache))) {
            if (trimRatio >= 100) {
                ((LruCache) this.cache).trimToSize(-1);
            } else {
                ((LruCache) this.cache).trimToSize(this.cache.size() / trimRatio);
            }
        }
    }
}

